﻿using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Linq;
using System.Reflection;
using Marmat.Common.Http.Attributes;

namespace Marmat.Common.Http
{
    class HttpApiInterceptor : Castle.DynamicProxy.IInterceptor
    {
        private readonly HttpApiConfig config;
        private readonly Type declaringType;

        public HttpApiInterceptor(HttpApiConfig config, Type type)
        {
            this.config = config;
            this.declaringType = type;
        }

        void IInterceptor.Intercept(IInvocation invocation)
        {
            LogMessage logMessage = new();
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            ApiActionDescriptor descriptor = new(this.declaringType, invocation.Method, invocation.Arguments);
            ApiActionContext context = new(config, descriptor);
            var returnType = invocation.Method.ReturnType;
            List<ActionFilterAttribute> actionFilters = new();
            List<ResultFilterAttribute> resultFilters = new();
            List<LoggingFilterAttribute> loggingFilters = new();

            try
            {
                // 解析请求的地址及方式。
                HttpMethodAddressInterceptor addressInterceptor = new();
                var requestUri = addressInterceptor.BuildUri(context);

                // 构建 HttpWebRequest 对象。
                request = (HttpWebRequest)WebRequest.Create(requestUri);
                request.Method = "GET";
                context.Request = request;
                logMessage.Request = request;

                // 解析请求的Http请求方式。
                var customAttributes = invocation.Method.GetCustomAttributes().ToArray();
                HttpMethodAttribute httpMethod = (HttpMethodAttribute)customAttributes.FirstOrDefault(x => x is HttpMethodAttribute);
                if (httpMethod is not null)
                    request.Method = httpMethod.Name;

                // 解析请求的Headers。
                HttpMethodHeadersInterceptor httpMethodHeadersInterceptor = new();
                httpMethodHeadersInterceptor.Invocation(context);

                #region 收集过滤器
                List<IHttpApiFilter> httpApiFilters = new List<IHttpApiFilter>();
                httpApiFilters.AddRange(customAttributes.Where(x => x is IHttpApiFilter).OfType<IHttpApiFilter>());
                customAttributes = invocation.Method.DeclaringType.GetCustomAttributes().ToArray();
                httpApiFilters.AddRange(customAttributes.Where(x => x is IHttpApiFilter).OfType<IHttpApiFilter>());

                if (invocation.Method.DeclaringType != this.declaringType)
                    httpApiFilters.AddRange(this.declaringType.GetCustomAttributes().Where(x => x is IHttpApiFilter).OfType<IHttpApiFilter>());
                httpApiFilters.AddRange(config.Filters);

                foreach (var filter in httpApiFilters)
                {
                    if (filter is ActionFilterAttribute actionFilter)
                        actionFilters.Add(actionFilter);
                    if (filter is ResultFilterAttribute resultFilter)
                        resultFilters.Add(resultFilter);
                    if (filter is LoggingFilterAttribute loggingFilter && loggingFilter.Enable)
                        loggingFilters.Add(loggingFilter);
                }

                if (resultFilters.Count == 0)
                    resultFilters.Add(new JsonResultFilterAttribute());
                actionFilters.Add(new HttpApiExecuteActionFilterAttribute(logMessage)); //添加具体的执行过程
                #endregion

                // 执行过滤器的OnActionExecuting函数。
                foreach (var apiAction in actionFilters)
                {
                    apiAction.OnActionExecuting(context);
                }

                // 获取请求结果。
                response = (HttpWebResponse)request.GetResponse();
                context.Response = response;

                #region 处理返回结果
                if (returnType == typeof(HttpWebResponse)) // 如果返回类型是 HttpWebResponse 类型，则不做任何转换，直接返回。
                {
                    invocation.ReturnValue = response;
                    //取消关闭流，方便后续用户读取。
                    response = null;
                    request = null;
                }
                else if (typeof(HttpResponseMessage).IsAssignableFrom(returnType)) // 如果返回类型是 HttpResponseMessage 类型，则构建 HttpResponseMessage 的实例并直接返回。
                {
                    invocation.ReturnValue = Activator.CreateInstance(returnType, new object[] { response });
                    //取消关闭流，方便后续用户读取。
                    response = null;
                    request = null;
                }
                else if (returnType == typeof(System.IO.Stream)) // 如果返回类型是 System.IO.Stream 类型，则在获取流之后不做任何转换，直接返回。
                {
                    invocation.ReturnValue = response.GetResponseStream();
                    //取消关闭流，方便后续用户读取。
                    response = null;
                    request = null;
                }
                else
                {
                    foreach (var result in resultFilters)
                    {
                        result.OnResultExecuting(context);
                    }
                    invocation.ReturnValue = context.ReturnValue; // 返回执行结果。
                }
                #endregion

                // 执行过滤器的OnActionExecuted函数。
                foreach (var apiAction in actionFilters)
                {
                    apiAction.OnActionExecuted(context);
                }
            }
            catch (WebException ex)
            {
                string message = ex.Message;
                if (ex.Response is not null)
                {
                    HttpWebResponse res = ex.Response as HttpWebResponse;
                    if (res.StatusCode == HttpStatusCode.InternalServerError)
                    {
                        System.IO.Stream stream = res.GetResponseStream();
                        var streamReader = new System.IO.StreamReader(stream, System.Text.Encoding.UTF8);
                        message = streamReader.ReadToEnd();
                        streamReader.Close();
                    }
                }
                throw new HttpApiException(context, message, ex);
            }
            catch (Exception ex)
            {
                throw new HttpApiException(context, ex.Message, ex);
            }
            finally
            {
                loggingFilters.ForEach(item => item.OnWriteLogAsync(context, logMessage));
                response?.Close();
                request?.Abort();
                context?.Dispose();
            }
        }
    }
}
